package com.zoro.dyformservice.support.filter.auth;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zoro.dyformservice.support.filter.auth.url.AuthTypeBo;
import com.zoro.dyformservice.support.filter.auth.url.AuthUrlSettingService;
import com.zoro.dyformservice.utils.CollectionUtils;
import com.zoro.dyformservice.utils.JsonUtil;
import com.zoro.dyformservice.utils.StringUtils;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

public class LoginFilter implements Filter {

    private static final String DEFAULT_DOMAIN = "http://localhost:8080";
    private static final String DEFAULT_LOGIN_URI = "/login";
    public static final String API_LOGIN = "/login";
    public static final String API_LOGOUT = "/logout";
    public static final String API_CURRENT_USER = "/currentUser";

    private final Set<String> ignoreUri;
    private final String domain;
    private final String loginUri;
    private final AuthUrlSettingService authUrlSettingService;

    public LoginFilter(String domain, String loginUri, Set<String> ignoreUriSet,
                       AuthUrlSettingService authUrlSettingService) {
        if (StringUtils.isBlank(domain)) {
            this.domain = DEFAULT_DOMAIN;
        } else {
            this.domain = domain;
        }
        if (StringUtils.isBlank(loginUri)) {
            this.loginUri = DEFAULT_LOGIN_URI;
        } else {
            this.loginUri = loginUri;
        }
        Set<String> ignoreSet = new HashSet<>();
        if (CollectionUtils.isNotEmpty(ignoreUriSet)) {
            ignoreSet.addAll(ignoreUriSet);
        }
        ignoreSet.add(API_CURRENT_USER);
        ignoreSet.add(API_LOGIN);
        ignoreSet.add(API_LOGOUT);
        ignoreSet.add("/favicon.ico");
        this.ignoreUri = Set.copyOf(ignoreSet);

        if (authUrlSettingService == null) {
            throw new IllegalArgumentException("authUrlSettingService can not be null!!");
        }
        this.authUrlSettingService = authUrlSettingService;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (!(servletRequest instanceof HttpServletRequest request)) {
            return;
        }
        LoginUser user = getLoginUser(servletRequest);
        LoginInfoContext.setLoginUser(user);

        try {
            if (this.isCheckLogin(request)) {
                if (user == null) {
                    this.notLogin(servletResponse);
                    return;
                }
            }
            filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            LoginInfoContext.clean();
        }

    }

    private boolean isCheckLogin(HttpServletRequest request) {
        String uri = request.getRequestURI();
        if (this.ignoreUri.contains(uri)) {
            return false;
        }
        AuthTypeBo authTypeBo = this.authUrlSettingService.getAuthTypeBo();

        return authTypeBo.isCheck(uri);
    }

    /**
     * 先从 Session 中获取登陆信息，如果没有。
     * 根据Request header中的Token 来解析，如果没有
     * ...
     *
     * @param servletRequest 请求
     * @return 登陆人
     */
    private LoginUser getLoginUser(ServletRequest servletRequest) {
        if (!(servletRequest instanceof HttpServletRequest request)) {
            return null;
        }
        LoginUser user = LoginInfoContext.getLoginUser(request.getSession());
        if (user != null) {
            return user;
        }
        return getLoginUserFromToken(request);
    }

    /**
     * 从 token 中解析
     *
     * @param request req
     * @return user
     */
    private LoginUser getLoginUserFromToken(HttpServletRequest request) {
        return null;
    }

    private void notLogin(ServletResponse servletResponse) {
        // 401
        if (servletResponse instanceof HttpServletResponse response) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            try {
                OutputStream os = response.getOutputStream();
                os.write(notLoginBody(this.domain, this.loginUri));
                os.flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static byte[] notLoginBody(String domain, String loginUri) {
        ObjectNode nodes = JsonUtil.makeJsonObject();
        nodes.put("Domain", domain);
        nodes.put("Uri", loginUri);
        return nodes.toString().getBytes(StandardCharsets.UTF_8);
    }
}
